#include <stdio.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <SDL/SDL.h>
#include <SDL/SDL_thread.h>

#ifdef __MINGW32__
#undef main /* Prevents SDL from overriding main() */
#endif


AVCodecContext  *pCodecContext;
AVCodec         *pCodec;
AVFrame         *pFrame; 
AVFrame         *pFrameRGB;
AVPacket        packet;
int             frameFinished;
FILE            * fp_bits;//文件流
struct SwsContext *img_convert_ctx; 



SDL_Overlay     *bmp;
SDL_Surface     *screen;
SDL_Rect        rect;
SDL_Event       event;


#define DISPLAY_TO_SCREEN      0
#define DISPLAY_TO_FILE        1
#define WHERE_TO_DISPLAY       DISPLAY_TO_SCREEN

#if WHERE_TO_DISPLAY==DISPLAY_TO_FILE
void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) 
{
  FILE *pFile;
  char szFilename[32];
  int  y;
  
  // Open file
  sprintf(szFilename, "/tmp/frame%d.ppm", iFrame);
  pFile=fopen(szFilename, "wb");
  if(pFile==NULL)
    return;
  
  // Write header
  fprintf(pFile, "P6\n%d %d\n255\n", width, height);
  
  // Write pixel data
  for(y=0; y<height; y++)
    fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);
  
  // Close file
  fclose(pFile);
}
#endif


int main(void)
{
    printf("decode start\n");
    
    //av_register_all();
    avcodec_register_all();
    
    if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
      fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
      exit(1);
    }


    av_init_packet(&packet);

    //pCodecContext = malloc(sizeof(AVCodecContext));
    
    pCodec = avcodec_find_decoder(CODEC_ID_MPEG4); 
    if (NULL == pCodec) 
    { 
	    printf("没有找到合适的编解码器\n");
	    return -1; 
    }
    else
    {
	    printf("找到 CODEC_ID_MPEG4 编解码器\n");
    }
	
#if LIBAVCODEC_VERSION_MAJOR <= 52
    pCodecContext = avcodec_alloc_context2(pCodec->type); 
#else
    pCodecContext = avcodec_alloc_context3(pCodec); 
#endif
    pCodecContext->pix_fmt = PIX_FMT_YUV420P; 
    pCodecContext->width = 352; 
    pCodecContext->height = 240;
	
#if LIBAVCODEC_VERSION_MAJOR <= 52
    if (avcodec_open(pCodecContext, pCodec) < 0)
#else
    if (avcodec_open2(pCodecContext, pCodec, NULL) < 0)
#endif
    {
	    printf("打开编解码器失败\n");
	    return -1; 
    } 
    else
    {
	    printf("打开编解码器成功\n");
    }
	
    printf("pCodecContext->codec_type=%d\n", pCodecContext->codec_type);
    printf("pCodecContext->codec_id=%d\n", pCodecContext->codec_id);
    printf("pCodecContext->coder_type=%d\n", pCodecContext->coder_type);
    printf("pCodecContext->flags=%d\n", pCodecContext->flags);
    printf("pCodecContext->width=%d\n", pCodecContext->width);
    printf("pCodecContext->height=%d\n", pCodecContext->height);
    printf("pCodecContext->pix_fmt=%d\n", pCodecContext->pix_fmt);

//#if LIBAVCODEC_VERSION_MAJOR >= 52
      // construct the scale context, conversing to PIX_FMT_RGB24  
    // 根据编码信息设置渲染格式  
#if WHERE_TO_DISPLAY==DISPLAY_TO_FILE	    
    img_convert_ctx = sws_getContext(pCodecContext->width, pCodecContext->height, pCodecContext->pix_fmt,   
            pCodecContext->width, pCodecContext->height, PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);  
#elif WHERE_TO_DISPLAY==DISPLAY_TO_SCREEN
    img_convert_ctx = sws_getContext(pCodecContext->width, pCodecContext->height, pCodecContext->pix_fmt,   
            pCodecContext->width, pCodecContext->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);  
#endif
    if(img_convert_ctx == NULL)  
    {  
        fprintf(stderr, "Cannot initialize the conversion context!\n");  
//      exit(1);  
        return -1;  
    }  
//#endif

    fp_bits = fopen("./test2_320x240.mp4", "rb");
    if(fp_bits == NULL)
    {
	printf("打开视频流文件错误\n");
	return -1;
    }
    else
    {
	printf("打开文件正常\n");
    }
	
    char bits[2*1024*1024];
    
    fread(bits, 1089173, 1, fp_bits);
    fclose(fp_bits);
    
    char* p = bits;
    
    printf("分配帧空间\n");
    // Allocate video frame
    pFrame=avcodec_alloc_frame();
    printf("分配帧空间完毕\n");
	

    

    
#if WHERE_TO_DISPLAY==DISPLAY_TO_SCREEN
  // Make a screen to put our video
#ifndef __DARWIN__
        screen = SDL_SetVideoMode(pCodecContext->width, pCodecContext->height, 0, 0);
#else
        screen = SDL_SetVideoMode(pCodecContext->width, pCodecContext->height, 24, 0);
#endif
  if(!screen) {
    fprintf(stderr, "SDL: could not set video mode - exiting\n");
    exit(1);
  }
  
  // Allocate a place to put our YUV image on that screen
  bmp = SDL_CreateYUVOverlay(pCodecContext->width,
				 pCodecContext->height,
				 SDL_YV12_OVERLAY,
				 screen);
  
  
#endif  
  
  
  
  
#if WHERE_TO_DISPLAY==DISPLAY_TO_FILE
    // Allocate an AVFrame structure
    pFrameRGB=avcodec_alloc_frame();
    if(pFrameRGB==NULL)
	return -1;
    int  numBytes;
    // Determine required buffer size and allocate buffer
    numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecContext->width,
				pCodecContext->height);
    uint8_t *buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));

    // Assign appropriate parts of buffer to image planes in pFrameRGB
    // Note that pFrameRGB is an AVFrame, but AVFrame is a superset
    // of AVPicture
    avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,
	      pCodecContext->width, pCodecContext->height);
#endif
    
    
    
    
    int pregress=0;//解码游标
    
    while(pregress < 1089173)
    {
	    packet.data = p+pregress;
	    packet.size = 1024*20;
	    pCodecContext->pix_fmt = PIX_FMT_YUV420P;
	    printf("解码开始\n");
	    int use_bytes = avcodec_decode_video2(pCodecContext, pFrame, &frameFinished, &packet);
	    pregress += use_bytes;
	    printf("已用%d字节\n", use_bytes);
	    printf("解码完毕\n");
#if WHERE_TO_DISPLAY==DISPLAY_TO_FILE	    
	    printf("开锁缩放\n");
	    sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0, pCodecContext->height, pFrameRGB->data, pFrameRGB->linesize);
	    printf("缩放完毕\n");
	    

	    // Save the frame to disk
	    printf("保存文件:\n");
	    SaveFrame(pFrameRGB, pCodecContext->width, pCodecContext->height, packet.data);
	    printf("保存完毕:\n");
#elif WHERE_TO_DISPLAY==DISPLAY_TO_SCREEN
	    if(frameFinished) 
	    {
		SDL_LockYUVOverlay(bmp);
		
		AVPicture pict;
		pict.data[0] = bmp->pixels[0];
		pict.data[1] = bmp->pixels[2];
		pict.data[2] = bmp->pixels[1];

		pict.linesize[0] = bmp->pitches[0];
		pict.linesize[1] = bmp->pitches[2];
		pict.linesize[2] = bmp->pitches[1];
	    
		sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0, pCodecContext->height, pict.data, pict.linesize);

		
		SDL_UnlockYUVOverlay(bmp);
		
		rect.x = 0;
		rect.y = 0;
		rect.w = pCodecContext->width;
		rect.h = pCodecContext->height;
		SDL_DisplayYUVOverlay(bmp, &rect);
	    }
	    usleep(70*1000);
#endif
    }
    
    
#if WHERE_TO_DISPLAY==DISPLAY_TO_SCREEN
    SDL_PollEvent(&event);
    switch(event.type) {
    case SDL_QUIT:
      SDL_Quit();
      exit(0);
      break;
    default:
      break;
    }
#endif

printf("end\n");
}

